﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;

namespace StateMachineViewerMobile.Runtime.Xml
{
    [XmlRoot("machine")]
    public class MachineXml
    {
        #region Properties

        [XmlAttribute("name")]
        public String Name { get; set; }

        [XmlArray("states")]
        [XmlArrayItem("state")]
        public List<String> States { get; set; }

        [XmlArray("symbols")]
        [XmlArrayItem("symbol")]
        public List<Char> Symbols { get; set; }

        [XmlArray("transactions")]
        [XmlArrayItem("transaction")]
        public List<TransactionXml> Transactions { get; set; }

        [XmlElement("initial")]
        public String InitialState { get; set; }

        [XmlArray("finals")]
        [XmlArrayItem("final")]
        public List<String> FinalStates { get; set; }

        #endregion

        #region Contrutors

        public MachineXml()
        {
            this.Name = "";
            this.InitialState = "";
            this.States = new List<string>();
            this.Symbols = new List<char>();
            this.Transactions = new List<TransactionXml>();
            this.FinalStates = new List<string>();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Deserializa o xml que representa o máquina de estados
        /// </summary>
        /// <param name="Xml">Xml que representa a máquina de estados</param>
        /// <returns>Instância da máquina de estados</returns>
        public static MachineXml FromXml(String Xml)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(MachineXml));
            return (MachineXml)serializer.Deserialize(new StringReader(Xml));
        }

        /// <summary>
        /// Serializa a máquina de estados no formato xml
        /// </summary>
        /// <returns>Xml que representa a máquina de estados</returns>
        public String ToXml()
        {
            String xml = String.Empty;

            try
            {
                XmlSerializer serializer = new XmlSerializer(typeof(MachineXml));

                MemoryStream memoryStream = new MemoryStream();
                serializer.Serialize(memoryStream, this);
                memoryStream.Seek(0, SeekOrigin.Begin);

                byte[] saida = new byte[memoryStream.Length];
                memoryStream.Position = 0;
                memoryStream.Read(saida, 0, (int)memoryStream.Length);
                memoryStream.Close();

                UTF8Encoding m_utf8 = new UTF8Encoding();
                xml = m_utf8.GetString(saida, 0, saida.Length);
            }
            catch (Exception ex)
            {
                xml = ex.Message;
            }

            return xml.Replace("<?xml version=\"1.0\"?>", string.Empty).Replace(" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"", string.Empty);

        }

        /// <summary>
        /// Realiza a validação da máquina de estados.
        /// </summary>
        public void IsValid()
        {
            // nome: permite qualquer nome, com comprimento maior que zero
            if (this.Name.Trim().Length == 0)
                throw new Exception("Você deve informar o nome do autômato.");
            
            // estado inicial: deve pertencer ao conjunto de estados
            if (this.InitialState.Trim().Length == 0)
                throw new Exception("Você deve informar o estado inicial.");
            if (!this.States.Contains(this.InitialState.Trim()))
                throw new Exception("O estado inicial deve pertencer ao conjunto de estados.");
            
            // Valida se estado inicial pertence ao conjunto de estados
            if (!this.States.Contains(this.InitialState))
                throw new Exception("O estado inicial deve pertencer ao conjunto de estados da máquina.");

            //
            if (this.Symbols.Count == 0)
                throw new Exception("Você deve informar ao menos um símbolo.");
            else
                if (this.Symbols.Count == 1 && this.Symbols[0].ToString().Length == 0)
                    throw new Exception("Você deve informar ao menos um símbolo.");

            //
            if (this.FinalStates.Count == 0)
                throw new Exception("Você deve informar ao menos um estado final.");
            else
                if (this.FinalStates.Count == 1 && this.FinalStates[0].Trim().Length == 0)
                    throw new Exception("Você deve informar ao menos um estado final.");
            
            // estados finais: devem pertencer ao conjunto de estados
            foreach (String sf in this.FinalStates)
                if (!this.States.Contains(sf.Trim()))
                    throw new Exception(String.Format("O estado final '{0}' deve pertencer ao conjunto de estados.", sf.Trim()));

            bool fromInitialState = false;

            // Valida as transições
            foreach (TransactionXml t in this.Transactions)
            {
                // Quando encontra uma conexão de partida do estado inicial
                if (t.StateIn.Trim().Equals(this.InitialState.Trim()))
                    fromInitialState = true;

                if (!this.States.Contains(t.StateIn.Trim()))
                    throw new Exception(String.Format("O estado atual '{0}' na transação {1} deve pertencer ao conjunto de estados.", t.StateIn, t.GetTransactionDescription()));

                if (!this.Symbols.Contains(t.Symbol))
                    throw new Exception(String.Format("O símbolo '{0}' na transação {1} deve pertencer ao conjunto de estados.", t.Symbol, t.GetTransactionDescription()));

                foreach (String s in t.StatesOut)
                    if (!this.States.Contains(s.Trim()))
                        throw new Exception(String.Format("O próximo estado '{0}' na transação {1} deve pertencer ao conjunto de estados.", s, t.GetTransactionDescription()));
            }

            //
            if (!fromInitialState)
                throw new Exception(String.Format("Deve existir ao menos uma transição partindo do estado inicial '{0}'.", this.InitialState));
  
            
            /*
            - transições: 
                - estado atual: deve pertencer ao conjunto de estados
                - simbolo: 1 caracter, deve pertencer ao conjunto de simbolos
                - próximos estados: devem pertencer ao conjunto de estados

            - estado inicial deve ter ao menos uma transição de origem
            - cada estados intermediarios(não final e não inicial): deve ter ao menos uma transição de origem, e uma de destino.
            - cada estado final: deve ter ao menos uma transição de destino
             * */
        }
        #endregion
    }
}
